package amani.wechat.platform.utils;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.log4j.Logger;
import org.jeewx.api.core.common.AccessToken;
import org.jeewx.api.core.exception.WexinReqException;
import org.jeewx.api.core.util.HttpRequestProxy;
import org.jeewx.api.wxbase.wxtoken.JwTokenAPI;

import amani.wechat.platform.services.impl.ConfigurationService;

import com.tencent.WXPay;
import com.tencent.common.Configure;
import com.tencent.common.MD5;
import com.tencent.common.RandomStringGenerator;
import com.tencent.common.Signature;
import com.tencent.common.XMLParser;

/**
 * 微信调用工具类
 * 
 * @author VV
 *
 */
public class WxUtils {
	private static Logger logger = Logger.getLogger(ConfigurationService.class);
	private static AccessToken token;

	private static final String REQUEST_JSAPI_TICKET_URL_KEY = "REQUEST_JSAPI_TICKET_URL"; 
	private static final String REQUEST_CARDAPI_TICKET_URL_KEY = "REQUEST_CARDAPI_TICKET_URL"; 

	private static final String WX_CERTIFICATE_DOMAIN_KEY = "WX_CERTIFICATE_DOMAIN";

	private static final String APPID_KEY = "APPID";
	private static final String MCH_ID_KEY = "MCH_ID";
	private static final String ENCODING_KEY_KEY = "ENCODING_KEY";
	private static final String CHECK_TOKEN_KEY = "CHECK_TOKEN";

	private static final String PAY_KEY_KEY = "PAY_KEY";

	private static final String APP_SCRECTE_KEY = "APP_SCRECTE";

	private static final String CACHE_KEY = "CACHE";

	private static final String PROPETY_FILE_PATH = "/WxUtils.properties";
	private static final String PROPETY_CACHE_FILE_PATH = "/Cache.properties";

	private static Map<String, CacheObject> cacheMap;

	private static Properties properties = new Properties();

	/**
	 * 将缓存数据序列化json写入properties
	 */
	private static void WriteCache() {
		Properties cacheProperties = new Properties();
		URL url = WxUtils.class.getClassLoader().getResource("");
		String path = "";
		InputStream in = null;
		try {
			path = URLDecoder.decode(url.getPath(),"utf-8");
			in = new FileInputStream(path + PROPETY_CACHE_FILE_PATH);
			cacheProperties.load(in);
		} catch (IOException e) {
			e.printStackTrace();

		} finally {
			try {
				if (in != null) {
					in.close();
				}
			} catch (IOException e) {
				return;
			}
		}
		OutputStream fos;
		try {
			fos = new FileOutputStream(path + PROPETY_CACHE_FILE_PATH);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return;
		}
		JSONArray cacehJsonArray = JSONArray.fromObject(cacheMap);
		cacheProperties.setProperty(CACHE_KEY, cacehJsonArray.toString());
		try {
			cacheProperties.store(fos, "");
		} catch (IOException e) {
			e.printStackTrace();
			return;
		} finally {
			try {
				fos.close();
			} catch (IOException e) {
				e.printStackTrace();
				return;
			}
		}
	}

	static {
		Configure
				.setHttpsRequestClassName("amani.wechat.platform.utils.HttpsRequest");

		properties = new Properties();
		Properties cacheProperties = new Properties();
		try {
			URL url = WxUtils.class.getClassLoader().getResource("");
			InputStream in = new FileInputStream(url.getPath()
					+ PROPETY_FILE_PATH);
			InputStream incache = new FileInputStream(url.getPath()
					+ PROPETY_CACHE_FILE_PATH);
			properties.load(in);
			cacheProperties.load(incache);
			in.close();
			incache.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		cacheMap = new HashMap<String, CacheObject>();
		if (cacheProperties.keySet().contains(CACHE_KEY)) {
			JSONArray cacheArray = JSONArray.fromObject(cacheProperties
					.get(CACHE_KEY));
			JSONObject obj = (JSONObject) cacheArray.get(0);
			java.util.Iterator keys = obj.keys();
			while (keys.hasNext()) {
				String key = (String) keys.next();
				JSONObject item = (JSONObject) obj.get(key);
				CacheObject cacheObj = new CacheObject();
				cacheObj.setExpiredtime(new Date(((JSONObject) item
						.get("expiredtime")).getLong("time")));
				cacheObj.setValue(item.get("value"));
				cacheMap.put(key, cacheObj);
			}
		}

		token = new AccessToken(properties.getProperty(APPID_KEY),
				properties.getProperty(APP_SCRECTE_KEY));
		String key = properties.getProperty(PAY_KEY_KEY);
		String appID = token.getAppid();
		String mchID = properties.getProperty(MCH_ID_KEY);
		String sdbMchID = "";
		String certLocalPath = "";
		String certPassword = "";
		WXPay.initSDKConfiguration(key, appID, mchID, sdbMchID, certLocalPath,
				certPassword);
	}

	/**
	 * 生成支付时需要的json数据
	 * 
	 * @param body
	 *            商品描述
	 * @param openid
	 * @param orderid
	 *            订单编号
	 * @param price
	 *            价格
	 * @return
	 * @throws Exception
	 */
	public static JSONObject GeneratePayJosn(String body, String openid,
			String orderid, double price, String payClientIp) throws Exception {
		WxPayData wxPayData = new WxPayData();
		wxPayData.setOpenid(openid);
		wxPayData.setNonce_str(RandomStringGenerator
				.getRandomStringByLength(32));

		wxPayData.setBody(body);
		wxPayData.setOut_trade_no(orderid);
		DecimalFormat df = new DecimalFormat("#");
		wxPayData.setTotal_fee(df.format(price * 100));
		wxPayData.setNotify_url(properties.getProperty("WEBSITE_ROOT")+ "/Pay/Notify");
		wxPayData.setTrade_type("JSAPI");
		wxPayData.setSpbill_create_ip(payClientIp);
		wxPayData.setMch_id(properties.getProperty(MCH_ID_KEY));
		wxPayData.setAppid(properties.getProperty(APPID_KEY));

		wxPayData.setSign(getSign(wxPayData));

		WebPagePayService webPagePayService = null;
		String xmlresoponse = "";
		webPagePayService = new WebPagePayService();
		xmlresoponse = webPagePayService.request(wxPayData);
		String prepay_id = "";
		if (!xmlresoponse.equals("")) {
			Map<String, Object> tempmap = XMLParser.getMapFromXML(xmlresoponse);
			if (tempmap.containsKey("prepay_id")) {
				prepay_id = (String) tempmap.get("prepay_id");
			}
		}
		net.sf.json.JSONObject obj = new net.sf.json.JSONObject();
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("appId", Configure.getAppid());
		map.put("timeStamp", create_timestamp());
		map.put("nonceStr", create_nonce_str());
		map.put("package", "prepay_id=" + prepay_id);
		map.put("signType", "MD5");
		map.put("paySign", Signature.getSign(map));

		obj.accumulate("appId", (String) map.get("appId"));
		obj.accumulate("timestamp", (String) map.get("timeStamp"));
		obj.accumulate("nonceStr", (String) map.get("nonceStr"));
		obj.accumulate("package", (String) map.get("package"));
		obj.accumulate("signType", "MD5");
		obj.accumulate("paySign", (String) map.get("paySign"));

		String resultString = obj.toString();
		return obj;
	}
	/**
	 * 获取回掉地址校验的Token
	 * @return
	 */
	public static String getCheckToken() {
		return properties.getProperty(CHECK_TOKEN_KEY);
	}
	
	/**
	 * 获取网站跟目录
	 * @return
	 */
	public static String getWebsiteRoot() {
		return properties.getProperty("WEBSITE_ROOT");
	}

	public static String getSign(Object o) throws IllegalAccessException {
		ArrayList<String> list = new ArrayList<String>();
		Class cls = o.getClass();
		Field[] fields = cls.getDeclaredFields();
		for (Field f : fields) {
			f.setAccessible(true);
			if (f.get(o) != null && !f.get(o).equals("")) {
				list.add(f.getName() + "=" + f.get(o) + "&");
			}
		}
		int size = list.size();
		String[] arrayToSort = list.toArray(new String[size]);
		Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < size; i++) {
			sb.append(arrayToSort[i]);
		}
		String result = sb.toString();
		result += "key=" + Configure.getKey();
		// Util.log("Sign Before MD5:" + result);
		result = MD5.MD5Encode(result).toUpperCase();
		// Util.log("Sign Result:" + result);
		return result;
	}
	public static String getCardHashSign(Map<String,Object> map){
        ArrayList<String> list = new ArrayList<String>();
        for(Map.Entry<String,Object> entry:map.entrySet()){
            if(!entry.getValue().equals("")){
                list.add((String)entry.getValue());
            }
        }
        int size = list.size();
        String [] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < size; i ++) {
            sb.append(arrayToSort[i]);
        }  
		String result = sb.toString(); 
        result = DigestUtils.shaHex(result); 
        return result;
    }


	/**
	 * 
	 * @param code
	 * @param openid
	 * @return
	 * @throws WexinReqException 
	 */
	public static JSONObject getCardExt(String cardId,String code, String openid) throws WexinReqException {
		Map<String, Object> map = new HashMap<String, Object>();

		map.put("card_id", cardId);

	//	map.put("app_id",  Configure.getAppid());
		map.put("code", code);
		map.put("openid", openid);
		map.put("timeStamp", create_timestamp());
		//map.put("nonce_str", create_nonce_str());
		map.put("api_ticket", properties.get(APP_SCRECTE_KEY));// GetWXCardApiTicket(GetAccessToken()));
		map.put("signature", getCardHashSign(map));
		
		net.sf.json.JSONObject obj = new net.sf.json.JSONObject();
		
		obj.accumulate("code",(String) map.get("code"));
		obj.accumulate("openid",(String) map.get("openid"));
		obj.accumulate("timestamp", (String) map.get("timeStamp"));
		//obj.accumulate("nonce_str", (String) map.get("nonce_str"));
		obj.accumulate("signature", (String) map.get("signature")); 
		String resultString = obj.toString();
		resultString=resultString.replaceAll("\"", "'");
		return obj;
	}

	/**
	 * token验证
	 * 
	 * @param parameters
	 *            微信服务器发送的请求数据集合（按照key的升序排列的集合）
	 * @return signature的集合
	 */
	public static Map<String, String> CheckReuqetParmeter(
			Map<String, String> parameters) {
		Map<String, String> ret = new HashMap<String, String>();
		String signature = "";
		String string1 = "";
		// System.out.println(string1);
		Set<String> keySet = parameters.keySet();
		Iterator<String> iter = keySet.iterator();
		while (iter.hasNext()) {
			String key = iter.next();
			string1 += parameters.get(key);
		}
		signature = DigestUtils.shaHex(string1);
		ret.put("signature", signature);
		return ret;
	}

	/**
	 * 获取微信的AccessToken
	 * 
	 * @return 返回AccessToken
	 * @throws WexinReqException
	 */
	public static String GetAccessToken() throws WexinReqException {
		String key = "AccessToken";
		if (cacheMap.containsKey(key)) {
			CacheObject catchObject = cacheMap.get(key);
			if (catchObject.isExpired()) {
				catchObject.setValue(JwTokenAPI.getAccessToken(
						token.getAppid(), token.getAppscret()));
				catchObject.setExpiredtime(7100 * 1000);
				WriteCache();
			}
		} else {
			CacheObject catchObject = new CacheObject();
			catchObject.setExpiredtime(7100 * 1000);
			catchObject.setValue(JwTokenAPI.getAccessToken(token.getAppid(),
					token.getAppscret()));
			cacheMap.put(key, catchObject);
			WriteCache();
		}
		return (String) cacheMap.get(key).getValue();
	}

	/**
	 * 获取JSAPI的签名时需要的Ticket
	 * 
	 * @return 返回签名需要的Ticket
	 */
	public static String GetWXJsapTicket(String accessToken) {
		String key = "JsapTicket";
		if (cacheMap.containsKey(key)) {
			CacheObject catchObject = cacheMap.get(key);
			if (catchObject.isExpired()) {
				HttpRequestProxy proxy = new HttpRequestProxy();
				String jsonResult = proxy.doGet(String.format(
						properties.getProperty(REQUEST_JSAPI_TICKET_URL_KEY),
						accessToken), "UTF-8");
				JSONObject obj = JSONObject.fromObject(jsonResult);
				catchObject.setValue(obj.getString("ticket"));
				catchObject.setExpiredtime(7100 * 1000);
				WriteCache();
			}
		} else {
			CacheObject catchObject = new CacheObject();
			HttpRequestProxy proxy = new HttpRequestProxy();
			String jsonResult = proxy.doGet(String.format(
					properties.getProperty(REQUEST_JSAPI_TICKET_URL_KEY),
					accessToken), "UTF-8");
			JSONObject obj = JSONObject.fromObject(jsonResult);
			catchObject.setValue(obj.getString("ticket"));
			catchObject.setExpiredtime(7100 * 1000);
			cacheMap.put(key, catchObject);
			WriteCache();
		}
		return (String) cacheMap.get(key).getValue();
	}
	public static String GetWXCardApiTicket(String accessToken) throws WexinReqException{
		String key = "CardApiTicket";
		if (cacheMap.containsKey(key)) {
			CacheObject catchObject = cacheMap.get(key);
			if (catchObject.isExpired()) {
				HttpRequestProxy proxy = new HttpRequestProxy();
				String jsonResult = proxy.doGet(String.format(
						properties.getProperty(REQUEST_CARDAPI_TICKET_URL_KEY),
						accessToken), "UTF-8");
				JSONObject obj = JSONObject.fromObject(jsonResult);
				catchObject.setValue(obj.getString("ticket"));
				catchObject.setExpiredtime(7100 * 1000);
				WriteCache();
			}
		} else {
			CacheObject catchObject = new CacheObject();
			HttpRequestProxy proxy = new HttpRequestProxy();
			String jsonResult = proxy.doGet(String.format(
					properties.getProperty(REQUEST_CARDAPI_TICKET_URL_KEY),
					accessToken), "UTF-8");
			JSONObject obj = JSONObject.fromObject(jsonResult);
			catchObject.setValue(obj.getString("ticket"));
			catchObject.setExpiredtime(7100 * 1000);
			cacheMap.put(key, catchObject);
			WriteCache();
		}
		return (String) cacheMap.get(key).getValue(); 
	}
	 

	/**
	 * 生成JSAPI所需要的签名数据集合
	 * 
	 * @param jsapi_ticket
	 * @param url
	 * @return
	 * @throws WexinReqException
	 */
	public static Map<String, String> JssdkSign(String url) throws WexinReqException {
		Map<String, String> ret = new HashMap<String, String>();
		String jsapi_ticket = GetWXJsapTicket(GetAccessToken());
		//String url = properties.getProperty(WX_CERTIFICATE_DOMAIN_KEY);
		// url = properties.getProperty(WX_CERTIFICATE_DOMAIN_KEY);
		String nonce_str = create_nonce_str();
		String timestamp = create_timestamp();
		String string1;
		String signature = "";
		// 注意这里参数名必须全部小写，且必须有序
		string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str
				+ "&timestamp=" + timestamp + "&url=" + url;

		signature = DigestUtils.shaHex(string1);
		ret.put("url", url);
		ret.put("jsapi_ticket", jsapi_ticket);
		ret.put("nonceStr", nonce_str);
		ret.put("timestamp", timestamp);
		ret.put("signature", signature);
		ret.put("appId", Configure.getAppid());
		return ret;

	}
	
	

	public static String GetAppId() {
		return properties.getProperty(APPID_KEY);
	}

	public static String GetAppScrecte() {
		return properties.getProperty(APP_SCRECTE_KEY);
	}

	private static String create_nonce_str() {
		return RandomStringGenerator.getRandomStringByLength(32);
	}

	private static String create_timestamp() {
		return Long.toString(System.currentTimeMillis() / 1000);
	}
}
